package emulator.GUI.joystick;

import java.awt.BasicStroke;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Stroke;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.Observable;
import java.util.Observer;

import emulator.hardware.io.peripherals.Joystick;
import emulator.hardware.io.peripherals.JoystickButton;

@SuppressWarnings("serial")
public class VicJoystickCanvas extends Canvas {
	private JoystickButton fire_pressed;
	private JoystickButton left_pressed;
	private JoystickButton right_pressed;
	private JoystickButton up_pressed;
	private JoystickButton down_pressed;
	private int center_x;
	private int center_y;
	private int inner_a;
	private int inner_b;
	private int outer_a;
	private int outer_b;
	private Rectangle inner_circle;
	private Rectangle left_circle;
	private Rectangle right_circle;
	private Rectangle up_circle;
	private Rectangle down_circle;
	
	public VicJoystickCanvas(Joystick joystick)
	{
		super();
		fire_pressed = joystick.getButton(0);
		left_pressed = joystick.getButton(1);
		right_pressed = joystick.getButton(2);
		up_pressed = joystick.getButton(3);
		down_pressed = joystick.getButton(4);
		addMouseListener(new MouseHandler());
		addMouseMotionListener(new MouseHandler());
		joystick.addObserver(new JoystickObserver());
	}
	
	/* (non-Javadoc)
	 * @see java.awt.Container#paint(java.awt.Graphics)
	 */
	@Override
	public void paint(Graphics g) {
		super.paint(g);
		Graphics2D g2d = (Graphics2D)g;
		// calc positions
		Rectangle outer_area = getBounds();
		outer_area.grow(-5,-5);
		Rectangle inner_area = getBounds();
		inner_area.grow(-inner_area.width/3,-inner_area.height/3);
		inner_circle = (Rectangle)inner_area.clone();
		inner_circle.grow(-inner_circle.width/4, -inner_circle.height/4);
		left_circle = (Rectangle)inner_circle.clone();
		left_circle.translate(-inner_area.width, 0);
		right_circle = (Rectangle)inner_circle.clone();
		right_circle.translate(inner_area.width, 0);
		up_circle = (Rectangle)inner_circle.clone();
		up_circle.translate(0, -inner_area.height);
		down_circle = (Rectangle)inner_circle.clone();
		down_circle.translate(0, inner_area.height);
		Stroke save_stroke = g2d.getStroke();
		// store positions for mouse actions.
		center_x = inner_area.x + inner_area.width/2;
		center_y = inner_area.y + inner_area.height/2;
		inner_a = inner_area.width/2;
		inner_b = inner_area.height/2;
		outer_a = outer_area.width/2;
		outer_b = outer_area.height/2;
		// draw background
	    g2d.setStroke(new BasicStroke(5)); // 5-pixel wide pen
		g2d.drawOval(outer_area.x, outer_area.y, outer_area.width, outer_area.height);
		g2d.drawOval(inner_area.x, inner_area.y, inner_area.width, inner_area.height);
		g2d.setStroke(save_stroke);
		// button states
		drawButtonStates(g2d);
	}

	private void drawButtonStates(Graphics2D g2d) {
		drawState(g2d, inner_circle, fire_pressed.getState());
		drawState(g2d, left_circle, left_pressed.getState());
		drawState(g2d, right_circle, right_pressed.getState());
		drawState(g2d, up_circle, up_pressed.getState());
		drawState(g2d, down_circle, down_pressed.getState());
	}

	private void drawState(Graphics2D g2d, Rectangle rectangle, boolean active) {
		g2d.setColor(active ? Color.GREEN : Color.WHITE);
		g2d.fillOval(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
		g2d.setColor(Color.BLACK);
		g2d.drawOval(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
	}

	private void handleMousePosition(MouseEvent arg0) {
		boolean left_state = false;
		boolean right_state = false;
		boolean up_state = false;
		boolean down_state = false;
		int rx = arg0.getPoint().x - center_x;
		int ry = arg0.getPoint().y - center_y;
		double rxi = rx/(double)inner_a;
		double ryi = ry/(double)inner_b;
		if (rxi*rxi + ryi*ryi > 1)
		{
			double rxo = rx/(double)outer_a;
			double ryo = ry/(double)outer_b;
			if (rxo*rxo + ryo*ryo < 1)
			{
				if (rx > inner_a)
					right_state = true;
				else if (rx < -inner_a)
					left_state = true;
				if (ry > inner_b)
					down_state = true;
				else if (ry < -inner_b)
					up_state = true;
			}
		}
		updateButton(left_state, left_pressed);
		updateButton(right_state, right_pressed);
		updateButton(up_state, up_pressed);
		updateButton(down_state, down_pressed);
	}

	private void updateButton(boolean state, JoystickButton button) {
		if (state != button.getState())
		{
			if (state)
				button.press();
			else
				button.release();
		}
	}

	class JoystickObserver implements Observer
	{
		@Override
		public void update(Observable joystick, Object event) {
			drawButtonStates((Graphics2D) getGraphics());
		}
	}
	
	class MouseHandler implements MouseListener, MouseMotionListener
	{
		@Override
		public void mouseClicked(MouseEvent arg0) {
		}
	
		@Override
		public void mouseEntered(MouseEvent arg0) {
		}
	
		@Override
		public void mouseExited(MouseEvent arg0) {
			fire_pressed.release();
			left_pressed.release();
			right_pressed.release();
			up_pressed.release();
			down_pressed.release();
		}
	
		@Override
		public void mousePressed(MouseEvent arg0) {
			if (arg0.getButton() == MouseEvent.BUTTON1)
				fire_pressed.press();
		}
	
		@Override
		public void mouseReleased(MouseEvent arg0) {
			if (arg0.getButton() == MouseEvent.BUTTON1)
				fire_pressed.release();
		}
	
		@Override
		public void mouseDragged(MouseEvent arg0) {
			handleMousePosition(arg0);
		}
	
		@Override
		public void mouseMoved(MouseEvent arg0) {
			handleMousePosition(arg0);
		}
	}
}
